/*
 * $Id: AbstractTransferable.java,v 1.2 2001/10/27 10:27:44 jppietsch Exp $
 * Copyright (C) 2001 Jan-Peter Pietsch
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package de.jppietsch.prefedit;


import de.jppietsch.error.Precondition;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.prefs.Preferences;


/**
 * A convenient base class for simple transferables which support only two
 * data flavors for strings and objects local to a virtual machine.
 *
 * @author Jan-Peter Pietsch
 * @version $Revision: 1.2 $
 */
public abstract class AbstractTransferable implements Transferable {

    // implementation of Transferable

    /**
     * Returns an array of data flavors for this transferable.
     *
     * @return Some flavors.
     */
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{flavor(),
                DataFlavor.stringFlavor};
    }

    /**
     * Does this transferable support a given data flavor?
     *
     * @param aFlavor which is requested.
     * @return This transferable supports <code>aFlavor</code>.
     */
    public boolean isDataFlavorSupported(DataFlavor aFlavor) {
        Precondition.argumentNotNull("aFlavor", aFlavor);

        return aFlavor.match(flavor()) ||
                aFlavor.match(DataFlavor.stringFlavor);
    }

    /**
     * Returns this object or a string representation of it.
     *
     * @param aFlavor how the result should taste.
     * @return This object or a string representation of it.
     */
    public Object getTransferData(DataFlavor aFlavor)
            throws UnsupportedFlavorException {
        Object result;

        if (aFlavor.match(flavor())) {
            result = this;
        } else {
            if (aFlavor.match(DataFlavor.stringFlavor)) {
                result = toString();
            } else {
                throw new UnsupportedFlavorException(aFlavor);
            }
        }

        return result;
    }

    // public methods

    /**
     * Puts this transferable to the system clipboard.
     */
    public void copy() {
        systemClipboard().setContents(this, null);
    }

    /**
     * May be overwritten to remove this from somewhere.
     * This implementation just calls {@link #copy}.
     */
    public void cut() {
        copy();
    }

    /**
     * Takes an instance of this class from the system clipboard.
     *
     * @param somePreferences is given to {@link #doPaste(Preferences)}.
     * @throws UnsupportedFlavorException If the clipboard contains something
     *                                    else but not an
     *                                    {@link AbstractTransferable}.
     */
    public static void paste(Preferences somePreferences)
            throws UnsupportedFlavorException, IOException {
        Precondition.argumentNotNull("somePreferences", somePreferences);
        AbstractTransferable result = null;
        Transferable trans = systemClipboard().getContents(null);
        DataFlavor[] flavors = trans.getTransferDataFlavors();

        for (int i = 0; i < flavors.length && result == null; i++) {
            if (AbstractTransferable.class.isAssignableFrom(
                    flavors[i].getRepresentationClass())) {
                result = (AbstractTransferable)
                        trans.getTransferData(flavors[i]);
            }
        }

        if (result == null) {
            throw new UnsupportedFlavorException(
                    new DataFlavor(AbstractTransferable.class,
                            "abstract transferable"));
        }

        result.doPaste(somePreferences);
    }

    // protected methods

    /**
     * Creates a special data flavor for this object.
     *
     * @return Data flavor for this object which depends on its concrete class.
     */
    protected DataFlavor flavor() {
        return new DataFlavor(getClass(), humanPresentableClassName());
    }

    /**
     * Returns a human presentable name for this class.
     * This is needed for the implementation of {@link #flavor()}.
     *
     * @return A human presentable name for the concrete class.
     */
    protected abstract String humanPresentableClassName();

    /**
     * Adds this transferable to the given preferences.
     *
     * @param somePreferences to which this is added.
     */
    protected abstract void doPaste(Preferences somePreferences);

    // private methods

    /**
     * Convenience method to return the system clipboard.
     *
     * @return The system clipboard from the default toolkit.
     */
    private static Clipboard systemClipboard() {
        return Toolkit.getDefaultToolkit().getSystemClipboard();
    }

}
